// Copyright (c) 2008-2010, Arne Claus
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 
// following conditions are met:
//
// - Redistributions of source code must retain the above copyright notice, this list of conditions and the following 
//   disclaimer.
// - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
//   disclaimer in the documentation and/or other materials provided with the distribution.
// - Neither the name Arne Claus nor the names of any contributors may be used to endorse or promote products
//   derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef __INCLUDED_CBL_PROFILE_H__
#define __INCLUDED_CBL_PROFILE_H__

#include "CBL_Defines.h"
#include "CBL_Singleton.h"
#include "CBL_IdMap.h"
#include "CBL_StaticVector.h"
#include "CBL_Cpu.h"
#include "CBL_Locks.h"

CB_SUBNAMESPACE_START(Debug)

// ----------------------------------------------------------------------------------
//  Profiling storage
// ----------------------------------------------------------------------------------

//! \class  CProfileManager
//! \brief	Profiling hub class.
//! \detail To enable profiling you have to define the CB_PROFILE_ENABLED Macro and
//!			Create an instance of this manager, at best at the start of you main
//!			function. CProfileManager can be created even if CB_PROFILE_ENABLED is
//!			not set, but the convenience macros will have no effect in this case.
class CProfileManager : public CSingleton<CProfileManager>
{
public:
	
	//! \brief  Add a measuring for the given Key
	//! \detail If the key already exists the calling count will be increased and
	//!			_TimeMs will be added to the total time.
	//! \param _Key    A unique name for your measuring block
	//! \param _TimeMs The elapsed time in milliseconds (see CPU::GetTimeMs)
	BASELIB_API void AddMeasurement(const char* _Key, double _TimeMs);
	
	//! \brief  Print a report to stdout.
	BASELIB_API void PrintReport();
	
private:
	
    CBL_SINGLETON_PRIVATE(CProfileManager);
	
	BASELIB_API CProfileManager();
	BASELIB_API virtual ~CProfileManager();
    
    CProfileManager(const CProfileManager&);				// forbidden
    CProfileManager& operator = (const CProfileManager&);	// forbidden

private:
	
	struct SProfileItem 
	{
		double      m_TimeUsed;
		uint32      m_Calls;
		const char* m_Name;
	};

	Cpu::CMutex					m_AddMutex;
	CIdMap						m_Keys;
	CStaticVector<SProfileItem> m_Items;
};

// ----------------------------------------------------------------------------------
//  Profiling helper
// ----------------------------------------------------------------------------------

//! \class  CProfileScope
//! \brief	Helper class for scope based measurements.
//! \detail You should never use this class directly, instead use the convenience
//!         Macros CB_PROFILE, CB_PROFILE_START amd CB_PROFILE_END.
//!			To measure a complete function just add CB_PROFILE("<FunctionNameKey>") 
//!			At the start of the function. To measure one or more specific blocks
//!			inside a function use CB_PROFILE_START("<BlockNameKey>") at start of the
//!			block and CB_PROFILE_END at it's end.
//!			To use CProfileScope and it's macros the CProfileManager singleton has
//!			to be created first.
//!			All profiling macros won't have an effect unless CBL_PROFILING_ENABLED is 
//!			being defined.
class CProfileScope
{
public:
	
	CProfileScope(const char* _Key)
	: m_Key(_Key)
	{ 
		m_Start = Cpu::GetTimeMs(); 
	}
	
	~CProfileScope()
	{
		double Elapsed = Cpu::GetTimeMs() - m_Start;
		CProfileManager::GetInstance().AddMeasurement(m_Key, Elapsed);
	}
	
private:
	
	const char* m_Key;
	double m_Start;
};

#ifdef CBL_PROFILING_ENABLED

#define CB_PROFILE(_Key) CB_SNS(Debug)::CProfileScope CBProfileScope(_Key);
#define CB_PROFILE_START(_Key) { CB_SNS(Debug)::CProfileScope CBProfileScope(_Key);
#define CB_PROFILE_END }

#else

#define CB_PROFILE(_Key)
#define CB_PROFILE_START(_Key) {
#define CB_PROFILE_END }

#endif

CB_SUBNAMESPACE_END

#endif